package com.cesc.demo.service;

import com.cesc.demo.mode.PageResult;
import com.cesc.demo.mode.Product;
import com.cesc.demo.repository.ProductReactiveMysqlRepository;
import com.cesc.demo.repository.ProductReactiveRedisRepositoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static org.springframework.data.relational.core.query.Criteria.where;

@Service
public class ProductService {
    private final Map<String, Product> products=new ConcurrentHashMap<>();

    @Autowired
    private  R2dbcEntityTemplate template;

    @Autowired
    private ProductReactiveRedisRepositoryImpl productReactiveRedisReposiory;
    @Autowired
    private ProductReactiveMysqlRepository productReactiveMysqlRepository;

    public Flux<Product> getProducts(){
        return Flux.fromIterable(this.products.values());
    }

    public Flux<Product> getProductsByIds(final Flux<String> ids){
        return ids.flatMap(id-> Mono.justOrEmpty(this.products.get(id)));
    }

    public Mono<Void> createOrUpdatePorduct(final Mono<Product> productMono){
        return productMono.doOnNext(product -> {
            products.put(product.getId()+"",product);
        })
                .thenEmpty(Mono.empty());
    }

    public Mono<Product> getProductById(final String id){
        return Mono.justOrEmpty(this.products.get(id));
    }

    public Flux<Product> listProductsFromRedis(){
        return productReactiveRedisReposiory.findAllProduct();
    }




    public Mono<Product> getProductByIdFromRedis(final String id){
        return productReactiveRedisReposiory.findProductById(id);
    }



    public Mono<Boolean> saveToRedis(Product product)
    {
        return productReactiveRedisReposiory.saveProduct(product);
    }

    public Mono<Product> deleteProduct(final String id){
        return Mono.justOrEmpty(this.products.remove(id));
    }

    @Transactional
    public Mono<Product> saveToDb(Product product)
    {

      return   this.productReactiveMysqlRepository.save(product).onErrorMap(e->{
          System.out.println("has error"); //用于记录日志
          System.out.println(e.toString());
          return e;
      });
    }

    public Mono<PageResult<Product>> findByCondition(String name, String code, int pageSize, int page) {


        int pageOffset=0;
        pageOffset=(page-1)*pageSize;
        Sort.Order order= Sort.Order.desc("id");
        Sort sort=Sort.by(order);

        Criteria criteria=Criteria.empty();
        if(name!=null&&name.length()>0) {
            Criteria criteriaName = where("productName").like("%" + name + "%");
            Criteria criteriaDescription=where("description").like("%" + name + "%");
            criteria=criteria.or(criteriaName).or(criteriaDescription);
        }
        if(code!=null&&code.length()>0) {
            Criteria criteriacode = where("productCode").like("%" + code + "%");
            criteria=criteria.and(criteriacode);

        }

        Mono<Long> totalRecordMono=  this.template.select(Product.class).matching(Query.query(criteria)).count();
        Flux<Product> productFlux=this.template.select(Product.class)
                .matching(Query.query(criteria).limit(pageSize).offset(pageOffset).sort(sort))
                .all();


        Mono<List<Product>> productListMono=productFlux.collectList();
        return Mono.zip(totalRecordMono,productListMono).flatMapMany(data->{
            PageResult<Product> pageResult=new PageResult();
            pageResult.setTotalPage(data.getT1());
            pageResult.setRecords(data.getT2());
            return Mono.justOrEmpty(pageResult);
        }).last();

    }
}
